延續昨天,我們已經完成了To Do List的新增與刪除功能。實戰演練中少不了的就是CRUD,今天我們要來完成剩下「修改」與「頁籤搜尋」功能!
▲如圖所示,點選當前項目進入編輯畫面,每個項目不會互相影響
Step1.在「新增功能」上,調整塞值放入todos的陣列元素,新增temptext
、edit
function sendText() {
if (content.value != "") {
num.value += 1
const todo = {
id: num.value,
text: content.value,
temptext: content.value, //加入暫存內容欄位,用來存放編輯狀態下的文字
status: false,
edit:false //加入編輯狀態布林值,用來判斷是否在編輯中
}
todos.value.push(todo)
content.value = ""
}
}
Step2.在Html上,加入一個input,用來當作編輯時的媒介
2-1.v-if
要設定todo.edit
等於true才會顯示(初始為false)
2-2.v-model
和todo.temptext
綁定,每個項目都會跟temptext繫結而不會互相影響
<input class="todo_input_edit" type="text" v-if="todo.edit" v-model="todo.temptext" />
Step3.新增編輯狀態時確定和取消按鈕,避免和原本元素搞混,使用顏色區分
3-1.該區塊一樣要設定v-if=todo.edit
3-2.在存檔button上加上@click.stop="saveTodo(todo.id)"
3-3.在取消button上加上@click.stop="cancleTodo(todo.id)"
<span v-if="todo.edit" style="float:right">
<!--編輯狀態下的button-->
<button @click.stop="saveTodo(todo.id)" class="btn_icon" type="button" style="color:green;margin-right:5px">✔</button> <!--存檔button-->
<button @click.stop="cancleTodo(todo.id)" class="btn_icon" type="button" style="color:darkred">✘</button> <!--取消button-->
</span>
Step4.li標籤加入@dblclick="editTodo(todo)"
,雙擊觸發編輯狀態
<li v-for="todo in todos" :key="todo.id" @dblclick="editTodo(todo)">
...
</li>
function editTodo(todo) {
if (!todo.edit && !todo.status) //尚未編輯且尚未完成才會觸發
todos.value = todos.value.map(onetodo => onetodo.id === todo.id ? { ...onetodo, edit: !onetodo.edit } : onetodo)
//改變edit的狀態為編輯中
}
Step5.實作確認功能
function saveTodo(id) {
todos.value = todos.value.map(todo => todo.id === id ? { ...todo, text: todo.temptext, edit: !todo.edit } : todo)
//確認存檔,將temptext的值更新到text、edit狀態改為false
}
Step6.實作取消功能
function cancleTodo(id) {
todos.value = todos.value.map(todo => todo.id === id ? { ...todo, temptext:todo.text , edit: !todo.edit } : todo)
//取消存檔,將text的值更新到temptext、edit狀態改為false
}
Step7.介面防呆:找到代辦項目前面的☐,加上v-if="!todo.edit"
,讓它在編輯時不要出現
<button @click.stop="changeTodo(todo.id)" v-else v-if="!todo.edit" type="button" class="btn_icon" style="float:left">☐</button> <!--未完成button-->
最後成果:
本日的大魔王出現了,沒想到又在這上面卡了好久…前面我們只靠著ref
和@click
事件打天下,一不小心腦袋就打結了~刪刪改改寫了好久,真的不能不請出computed
啊!
Step1.把computed
import進來,新增頁籤響應式陣列資料、新增一個變數紀錄當前頁籤位置
import { ref, computed } from 'vue'
const action = ref("all") //紀錄當前頁籤位置,預設為全部
const navitem = ref([
{ name: "全部", value: "all" },
{ name: "進行中", value: "now" },
{ name: "已完成", value: "pass" }
])
Step2.html頁籤改寫,判斷為當前頁籤時給予一個active
的class
<div class="nav_block">
<button v-for="item in navitem" :class="{'active' : action == item.value}" class="nav_item" @click="action=item.value">{{item.name}}</button>
</div>
Step3.加入filtertodo
,監聽todos計算結果並同時篩選當前頁籤狀態進行過濾
const filtertodo = computed(function () {
switch (action.value) {
case "all": {
return todos.value.filter(todo => true)
}
case "now": {
return todos.value.filter(todo => !todo.status)
}
case "pass": {
return todos.value.filter(todo => todo.status)
}
default: {
return todos.value
}
}
})
Step?.最後的最後,記得把html中的todos換成filtertodo
<ul v-if="filtertodo.length > 0" class="todo_item">
<li v-for="todo in filtertodo" :key="todo.id" @dblclick="editTodo(todo)">
...
</li>
</ul>
<div class="todolist">
<div class="todo_wrapper">
<h1 style="border-bottom:1px dotted">To Do List</h1>
<br>
<div class="add_block">
<input v-model="content" class="todo_input" type="text" placeholder="請輸入文字" />
<button @click="sendText()" class="add_item">加入</button>
</div>
<div class="nav_block">
<button v-for="item in navitem" :class="{'active' : action == item.value}" class="nav_item" @click="action=item.value">{{item.name}}</button>
</div>
<ul v-if="filtertodo.length > 0" class="todo_item">
<li v-for="todo in filtertodo" :key="todo.id" @dblclick="editTodo(todo)">
<button @click.stop="changeTodo(todo.id)" v-if="todo.status" class="btn_icon" type="button" style="float:left">✔</button> <!--完成button-->
<button @click.stop="changeTodo(todo.id)" v-else v-if="!todo.edit" type="button" class="btn_icon" style="float:left">☐</button> <!--未完成button-->
<input class="todo_input_edit" type="text" v-if="todo.edit" v-model="todo.temptext" />
<label :class="{ 'line-through': todo.status }" v-else style="margin-left: 5px; cursor: pointer;">{{todo.text}}</label>
<span v-if="todo.edit" style="float:right">
<!--編輯狀態下的button-->
<button @click.stop="saveTodo(todo.id)" class="btn_icon" type="button" style="color:green;margin-right:5px">✔</button> <!--存檔button-->
<button @click.stop="cancleTodo(todo.id)" class="btn_icon" type="button" style="color:darkred">✘</button> <!--取消button-->
</span>
<button @click.stop="deleteTodo(todo.id)" v-else class="btn_icon" type="button" style="float:right">✘</button> <!--刪除button-->
</li>
</ul>
<ul class="todo_item" v-else>
<li>沒有資料</li>
</ul>
</div>
</div>
li {
width: 100%;
height: 50px;
list-style: none;
background-color: #ffffff;
padding: 13px;
cursor: pointer;
}
li:hover {
background-color: #cacaca;
}
.todolist {
min-height: 100vh;
display: flex;
align-items: center;
}
.todo_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items:center;
background-color: #ededed;
height:500px;
width:500px;
border-radius:5px;
}
.add_block {
display: inline-flex;
width: 85%;
justify-content: space-around
}
.todo_input {
border: none;
outline-style: none;
padding: .375rem .75rem;
width: 85%;
}
.todo_input_edit {
border: 1px solid #cacaca;
border-radius:5px;
outline-style: none;
padding: .375rem .75rem;
width: 85%;
margin-top:-5px;
}
.add_item {
border: none;
background-color: #808080;
margin-left: 10px;
color: aliceblue;
cursor: pointer;
padding: .375rem .75rem;
}
.nav_block {
margin-top: 15px;
border-bottom: 1px solid #808080;
width: 100%
}
.nav_item {
border: none;
background-color: #ffffff;
margin-left: 10px;
color:#808080;
cursor: pointer;
padding: .375rem .75rem
}
.todo_item {
width: 85%;
height: 300px;
overflow-y: auto;
margin-top: 20px;
scrollbar-width: thin;
padding: inherit;
}
.btn_icon {
background: none;
border: none;
cursor: pointer;
}
.line-through {
text-decoration:line-through
}
.active {
background-color: #808080;
color: white
}
import { ref, computed } from 'vue'
const num = ref(0) //作為todolist中的id計數
const content = ref('') //和v-model進行介面input輸入框的資料雙向綁定
const todos = ref([])
const action = ref("all")
const navitem = ref([
{ name: "全部", value: "all" },
{ name: "進行中", value: "now" },
{ name: "已完成", value: "pass" }
])
const filtertodo = computed(function () {
switch (action.value) {
case "all": {
return todos.value.filter(todo => true)
}
case "now": {
return todos.value.filter(todo => !todo.status)
}
case "pass": {
return todos.value.filter(todo => todo.status)
}
default: {
return todos.value
}
}
})
function sendText() {
if (content.value != "") {
num.value += 1
const todo = {
id: num.value,
text: content.value,
temptext: content.value,
status: false,
edit:false
}
todos.value.push(todo)
content.value = ""
}
}
function deleteTodo(id) {
todos.value = todos.value.filter(todo => todo.id !== id)
}
function changeTodo(id) {
todos.value = todos.value.map(todo => todo.id === id ? { ...todo, status: !todo.status } : todo)
}
function editTodo(todo) {
if (!todo.edit && !todo.status) //尚未編輯且尚未完成才會觸發
todos.value = todos.value.map(onetodo => onetodo.id === todo.id ? { ...onetodo, edit: !onetodo.edit } : onetodo)
}
function cancleTodo(id) {
todos.value = todos.value.map(todo => todo.id === id ? { ...todo, temptext:todo.text , edit: !todo.edit } : todo)
}
function saveTodo(id) {
todos.value = todos.value.map(todo => todo.id === id ? { ...todo, text: todo.temptext, edit: !todo.edit } : todo)
}
一卡關就卡了好幾小時甚至快整天時間…(還想著每天都要超前打一些明天的內容,真是辦不到:),看來離熟練還有很長一段路要走(淚目)但還是要加油!
P.S.雖然一度又快趕不出來今天的進度,但中途還是去看了今天上映的我的英雄學院:You're Next劇場版
參考資料
竹白筆記本—計算 & 監聽
Vue.js - 實例-ToDoList